package org.example.gradlemultipledatasourcedemo.config;

import com.zaxxer.hikari.HikariDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import java.util.List;

@Configuration
public class DynamicDataSourceAutoRegistrar implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private Environment environment;

    /**
     * BeanDefinitionRegistryPostProcessor 是一个 非常早期 执行的扩展点。
     * Spring 在调用它时：
     *
     * 💡 Spring 容器中的 Bean 还没有被实例化，也没有进行依赖注入。
     *
     * 也就是说，这个阶段：
     *
     * @Resource、@Autowired、@Value 等依赖注入机制都还没生效；
     *
     * 你能拿到的唯一注入对象是 Environment；
     *
     * 所以 configs 始终为 null。
     */
//    @Resource
//    private MultiDataSourceProperties configs;


    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        // 先删除 Spring Boot 自动注册的默认 DataSource（避免冲突）
        if (registry.containsBeanDefinition("dataSource")) {
            registry.removeBeanDefinition("dataSource");
            System.out.println("⚙️ 已移除 Spring Boot 默认数据源 BeanDefinition");
        }

        MultiDataSourceProperties configs = Binder.get(environment)
//                .bind("spring.datasource.configs", org.springframework.core.ResolvableType.forClassWithGenerics(List.class, DataSourceConfigProperties.class))
                .bind("spring.datasource", MultiDataSourceProperties.class)
                .orElse(null);

        if (configs == null || null == configs.getConfigs()) {
            throw new IllegalStateException("No datasource configurations found under spring.datasource.configs");
        }
        List<DataSourceConfigProperties> configs1 = configs.getConfigs();
        for (int i = 0; i < configs1.size(); i++) {
            DataSourceConfigProperties conf = configs1.get(i);

            // 1️⃣ 注册 DataSource Bean
            GenericBeanDefinition dsBeanDef = new GenericBeanDefinition();
            dsBeanDef.setBeanClass(HikariDataSource.class);
            dsBeanDef.setScope(BeanDefinition.SCOPE_SINGLETON);
            dsBeanDef.getPropertyValues().add("jdbcUrl", conf.getUrl());
            dsBeanDef.getPropertyValues().add("username", conf.getUsername());
            dsBeanDef.getPropertyValues().add("password", conf.getPassword());
            dsBeanDef.getPropertyValues().add("driverClassName", conf.getDriverClassName());

            String dsBeanName = conf.getName() + "DataSource";
            registry.registerBeanDefinition(dsBeanName, dsBeanDef);

            // 2️⃣ 注册 SqlSessionFactory Bean
            GenericBeanDefinition factoryDef = new GenericBeanDefinition();
            factoryDef.setBeanClass(SqlSessionFactoryBean.class);
            factoryDef.getPropertyValues().add("dataSource", new RuntimeBeanReference(dsBeanName));
            String factoryBeanName = conf.getName() + "SqlSessionFactory";
            registry.registerBeanDefinition(factoryBeanName, factoryDef);

            // 3️⃣ 注册 MapperScannerConfigurer Bean
            GenericBeanDefinition scannerDef = new GenericBeanDefinition();
            scannerDef.setBeanClass(MapperScannerConfigurer.class);
            scannerDef.getPropertyValues().add("basePackage", conf.getBasePackage());
            scannerDef.getPropertyValues().add("sqlSessionFactoryBeanName", factoryBeanName);
            registry.registerBeanDefinition(conf.getName() + "MapperScannerConfigurer", scannerDef);

            System.out.printf("✅ 数据源 [%s] 注册完成，扫描包：%s%n", conf.getName(), conf.getBasePackage());

            // 4️⃣ 第一个数据源作为默认
            if (i == 0) {
                registry.registerAlias(dsBeanName, "dataSource");
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 无需处理
    }

}
